package com.zhglxt.activiti.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zhglxt.activiti.entity.ModelEntityDto;
import com.zhglxt.activiti.service.ActModelService;
import com.zhglxt.common.core.entity.AjaxResult;
import com.zhglxt.common.core.page.TableDataInfo;
import com.zhglxt.common.core.text.Convert;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.persistence.entity.DeploymentEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.rest.editor.model.ModelEditorJsonRestResource;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.activiti.editor.constants.ModelDataJsonConstants.MODEL_ID;
import static org.activiti.editor.constants.ModelDataJsonConstants.MODEL_NAME;
/**
 * 流程模型管理 服务层实现层
 * @author liuwy
 */
@Service
public class ActModelServiceImpl implements ActModelService {

    private static final Logger LOGGER=LoggerFactory.getLogger(ActModelServiceImpl.class);
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;


    /**
     * 修改模型信息，保存修改信息时调用
     *
     * @param model    模型信息
     * @param json_xml json参数
     * @param svg_xml  xml参数
     */
    @Override
    public void update(Model model, String json_xml, String svg_xml) {
//        LOGGER.info("修改模型信息");
        try {
            repositoryService.saveModel(model);
            repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));
            InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8"));
            TranscoderInput input = new TranscoderInput(svgStream);

            PNGTranscoder transcoder = new PNGTranscoder();
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            TranscoderOutput output = new TranscoderOutput(outStream);

            transcoder.transcode(input, output);
            final byte[] result = outStream.toByteArray();
            repositoryService.addModelEditorSourceExtra(model.getId(), result);
            outStream.close();
        } catch (Exception e) {
            LOGGER.error("保存模型失败");
            throw new ActivitiException("保存模型失败，模型ID=" + model.getId(), e);

        }
    }

    /**
     * 查询模型编辑器,编辑流程模型时调用
     *
     * @param modelId 模型ID
     * @return json信息
     */
    @Override
    public ObjectNode selectWrapModelById(String modelId) {
//        LOGGER.info("查询模型编辑器,modelId:{}",modelId);
        ObjectNode modelNode = null;
        Model model = repositoryService.getModel(modelId);
        if (model != null) {
            try {
                //
                if (StringUtils.isNotEmpty(model.getMetaInfo())) {
                    modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
                } else {
                    modelNode = objectMapper.createObjectNode();
                    modelNode.put(MODEL_NAME, model.getName());
                }
                modelNode.put(MODEL_ID, model.getId());
                ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
                modelNode.set("model", editorJsonNode);

            } catch (Exception e) {
//                LOGGER.error("创建模型json失败", e);
                throw new ActivitiException("创建模型json失败，模型ID=" + modelId, e);
            }
        }
        return modelNode;
    }

    /**
     * 查询模型信息
     *
     * @param modelId 模型ID
     * @return 模型信息
     */
    @Override
    public Model selectModelById(String modelId) {
        return repositoryService.getModel(modelId);
    }

    /**
     * 根据模型ID批量删除
     *
     * @param ids 需要删除的数据ID
     * @return
     */
    @Override
    public boolean deleteModelIds(String ids) {
        boolean result = true;
        try {
            String[] modelIds = Convert.toStrArray(ids);
            // 批量删除循环删
            for (String modelId : modelIds) {
                repositoryService.deleteModel(modelId);
            }
        } catch (Exception e) {
            result = false;
            throw new ActivitiException("删除模型失败，模型ID=" + ids, e);
        }
        return result;
    }

    /**
     * 获取资源文件信息
     *
     * @param modelId 模型ID
     * @return 资源文件信息
     */
    @Override
    public byte[] getModelEditorSource(String modelId) {
//        LOGGER.info("获取资源文件信息");
        return repositoryService.getModelEditorSource(modelId);
    }


    /**
     * 查询模型列表
     *
     * @param modelEntityDto 模型信息
     * @return 模型集合
     */
    @Override
    public TableDataInfo selectModelList(ModelEntityDto modelEntityDto) {
//        LOGGER.info("查询模型列表");
//        LOGGER.info("查询参数是：{}",modelEntityDto);
        TableDataInfo data = new TableDataInfo();
        ModelQuery modelQuery = repositoryService.createModelQuery();

        // 查询模型分类
        if (StringUtils.isNotEmpty(modelEntityDto.getCategory())) {
            modelQuery.modelCategory(modelEntityDto.getCategory());
        }
        /**
         * 设置总数
         */
        data.setTotal(modelQuery.count());
        /**
         * 排序后分页
         */
        data.setRows(modelQuery.orderByModelId().desc().listPage(modelEntityDto.getPageNum(), modelEntityDto.getPageSize()));
//        LOGGER.info("查询结果是：{}",data);
        return data;

    }



    /**
     * 部署流程
     */
    @Transactional(readOnly = false)
    @Override
    public AjaxResult myDeployProcess(String id) {
        try {
//            LOGGER.info("获取指定的id的Model:id是{}",id);
            // 获取id对应的模型,查询ACT_RE_MODEL
            org.activiti.engine.repository.Model modelData = repositoryService.getModel(id);

            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();

            String modelId = modelData.getId();
//            LOGGER.info("从查询的ModelData中获取的的ModelId是:{}",modelId);
            // 获取模型的资源文件,查询ACT_RE_MODEL，ACT_RE_MODEL
            byte[] modelEditorSource = repositoryService.getModelEditorSource(modelId);
//            LOGGER.info("将资源文件转换成json对象");
            // 将资源文件转换成json对象
            JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
//            LOGGER.info("将模型的json对象转换成BpmnMOdel对象");
            // 将模型的json对象转换成BpmnMOdel对象
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);

//            LOGGER.info("将BpmnModel对象转换成xml");
            // 将BpmnModel对象转换成xml
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

            // bpmn数据内容
            String bpmpStrings = new String(bpmnBytes);
//            LOGGER.info("bpmn数据内容是：{}",bpmpStrings);

//            LOGGER.info("获取processName:");
            String processName = modelData.getName();
//            LOGGER.info("ActModelController：：myDeployProcess方法的processName--1:{}",processName);
            if (!StringUtils.endsWith(processName, ".bpmn20.xml")) {
                processName += ".bpmn20.xml";
            }
//            LOGGER.info("ActModelController：：myDeployProcess方法的processName--2:{}，modelData.getName():{}",processName,modelData.getName());


            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);

            // 通过流的方式部署，新增记录修改ACT_RE_PROCDEF，ACT_RE_DEPLOYMENT，ACT_GE_BYTEARRAY这三个主表
//            LOGGER.info("部署,创建流程");
            String category = modelData.getCategory();
//            LOGGER.info("category:{}",category);
            // todo modelData.getTenantId()  部署流程的时候同时设置参数
            Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).category(category).tenantId(modelData.getTenantId())
                    .addInputStream(processName, in).deploy();
            //					.addString(processName, new String(bpmnBytes)).deploy();
//            LOGGER.info("获取流程模型部署ID：");
            String deploymentId = deployment.getId();
//            LOGGER.info("流程模型部署后发布对象deployment是:{}",deployment);
//            LOGGER.info("流程模型部署后获取的部署ID是:{}",deploymentId);
//
//            LOGGER.info("查询部署ID为{}流程的定义信息：",deploymentId);
            // 设置流程分类
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).list();
//            LOGGER.info("部署ID为{}的流程列表是：{}",deploymentId,list);
//            LOGGER.info("部署ID为{}的部署流程数为:{}",deploymentId,list.size());

            if (list.size() == 0) {
                return AjaxResult.error("部署失败，没有流程。");
            }

            for (ProcessDefinition processDefinition : list) {
//                LOGGER.info("设置流程分类");
//                LOGGER.info("要设置流程分类为：{}",modelData.getCategory());
//                LOGGER.info("processDefinition:{}",processDefinition);
                repositoryService.setProcessDefinitionCategory(processDefinition.getId(), modelData.getCategory());
//				AjaxResult.success("部署成功，流程ID=" + processDefinition.getId());

            }
            return AjaxResult.success("部署成功");
        } catch (Exception e) {
            LOGGER.error("设计模型图不正确，请检查模型正确性，模型ID=" + id);
            throw new ActivitiException("设计模型图不正确，请检查模型正确性，模型ID=" + id, e);
        }
    }

    /**
     * 添加模型
     *
     * @throws UnsupportedEncodingException
     */
    @Transactional(readOnly = false)
    @Override
    public Model create(Map<String, Object> paramMap) throws UnsupportedEncodingException {

        // 模型对象，创建后返回
        Model modelData = repositoryService.newModel();

        System.out.println("modelDate::"+modelData);
        //模型描述信息
        String description = StringUtils.defaultString(paramMap.get("description").toString());
        // 模型key
        modelData.setKey(StringUtils.defaultString(paramMap.get("key").toString()));
        // 模型文件名称
        modelData.setName(paramMap.get("name").toString());
        // 模型分类
        modelData.setCategory(paramMap.get("category").toString());
        // 模型版本信息,包装类型自动转换，string和long转换
          modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));

        //设置模型mateinfo,act_re_model表metainfo字段
        ObjectNode modelObjectNode = objectMapper.createObjectNode();

        modelObjectNode.put(MODEL_NAME,modelData.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION,modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION,description);
        modelData.setMetaInfo(modelObjectNode.toString());
//        LOGGER.info("modeData:{}",modelData);
        /**
         * 保存模型数据信息
         */
        repositoryService.saveModel(modelData);


        /**
         * 创建模型时完善ModelEditorSource
         * 保存模型可编辑资源信息，保存到act_ge_bytearray表中，在act_re_model保存关联字段
         */
        ObjectNode stencilset = objectMapper.createObjectNode();
        // 模型命名空间
        stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");

        ObjectNode properties = objectMapper.createObjectNode();
        // 设置流程设计者名字字段
        properties.put("process_author", "sys");

        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        editorNode.put("stencilset", stencilset);
        editorNode.put("properties", properties);

        System.out.println("editorNode.toString()::"+editorNode.toString());

        repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));

        return modelData;
    }
}
